#include <errno.h>
#include "server.h"

/*=================================================客户端连接类=================================================*/
ClientConnection::ClientConnection(std::shared_ptr<Socket> sock) : client_socket(sock), is_logged_in(false) {}
std::shared_ptr<Socket> ClientConnection::getclientSocket() { return this->client_socket; }
int ClientConnection::getSocketFd() const { return client_socket->getFd(); }
void ClientConnection::send(const std::string& message)
{
    ::send(client_socket->getFd(), message.c_str(), message.length(), 0);
}
void ClientConnection::setLoggedInUser(std::shared_ptr<User> user)
{
    logged_in_user = user;
    is_logged_in = (user != nullptr);
}

std::shared_ptr<User> ClientConnection::getLoggedInUser() const { return logged_in_user; }


bool ClientConnection::isLoggedIn() const { return is_logged_in; }



/*=================================================用户管理类=================================================*/
void UserManager::loadUsers()          // 从文件加载所有用户数据
{
    // 使用互斥锁保护用户数据，确保线程安全
    std::lock_guard<std::mutex> lock(users_mutex);

    // 打开用户数据文件
    std::ifstream ifs(user_data_file);

    // 检查文件是否成功打开
    if (!ifs.is_open())
    {
        // 输出警告信息（非致命错误，系统可继续运行）
        std::cerr << "警告: 用户数据文件未找到或无法打开: " << user_data_file << std::endl;
        return;
    }

    std::string line;
    next_user_id = 0; // 重置并重新计算最大ID

    // 逐行读取文件内容
    while (std::getline(ifs, line))
    {
        User user;

        // 尝试将读取的行反序列化为User对象
        if (user.deSerialize(line) == 0) // 反序列化成功
        {
            // 将用户添加到已注册用户映射表中（键为用户名）
            registered_users[user.getName()] = user;

            // 更新下一个用户ID（确保新用户ID递增）
            if (user.getId() >= next_user_id)
            {
                next_user_id = user.getId() + 1;
            }
        }
    }

    // 关闭文件（RAII机制会自动关闭，但显式关闭更清晰）
    ifs.close();

    // 处理文件为空的情况（新系统或文件被清空）
    if (next_user_id == 0 && registered_users.empty())
    {
        next_user_id = 1; // 从1开始分配用户ID
    }
    // 处理文件存在但所有行解析失败的情况
    else if (registered_users.empty())
    {
        next_user_id = 1; // 同样从1开始分配用户ID
    }
}
void UserManager::saveUsers()       // 将用户数据保存到文件
{
    // std::lock_guard<std::mutex> lock(users_mutex);//这里自死锁了
    // 打开用户数据文件，准备写入（会覆盖原有内容）
    std::ofstream ofs(user_data_file);

    // 检查文件是否成功打开
    if (!ofs.is_open())
    {
        // 输出错误信息（致命错误，数据无法保存）
        std::cerr << "错误: 无法打开用户数据文件进行写入: " << user_data_file << std::endl;
        return;
    }

    // 遍历所有已注册用户
    for (auto it = registered_users.begin(); it != registered_users.end(); ++it)
    {
        // 调用用户对象的序列化方法，并将结果写入文件
        // 每行包含一个用户的序列化数据
        ofs << it->second.serialize();
    }

    // 关闭文件（RAII机制会自动关闭，但显式关闭更清晰）
    ofs.close();
}

// 构造函数：初始化用户管理器，接收用户数据文件路径作为参数
UserManager::UserManager(const std::string& filename)
    : user_data_file(filename), next_user_id(1) // 初始化成员变量：文件名和初始用户ID
{
    loadUsers(); // 构造对象时立即加载用户数据（从指定文件读取并解析）
}

// 注册新用户
bool UserManager::registerUser(const std::string& name, const std::string& password, const std::string& describe)
{
    // 加锁确保线程安全，防止多线程同时注册相同用户名
    std::lock_guard<std::mutex> lock(users_mutex);

    // 检查用户名是否已存在
    if (registered_users.count(name))
    {
        return false; // 用户已存在，注册失败
    }

    // 创建新用户对象，使用next_user_id作为ID，然后递增ID
    // 后置++：先用当前值创建用户，再将next_user_id加1
    User newUser(next_user_id++, name, password, describe);

    // 将新用户添加到用户映射表中（键为用户名）
    registered_users[name] = newUser;

    // 注册成功后立即保存数据到文件，确保持久化
    saveUsers();

    return true; // 注册成功
}

// 用户登录验证
std::shared_ptr<User> UserManager::loginUser(const std::string& name, const std::string& password)
{
    // 加锁确保线程安全，防止验证过程中用户数据被修改
    std::lock_guard<std::mutex> lock(users_mutex);

    // 在用户映射表中查找指定用户名
    auto it = registered_users.find(name);

    // 检查用户是否存在且密码匹配
    if (it != registered_users.end() && it->second.getPassword() == password)
    {
        // 返回用户对象的共享指针（副本）
        return std::make_shared<User>(it->second);
    }

    return nullptr; // 登录失败，返回空指针
}

// 根据用户名查找并返回一个用户对象的共享指针。
std::shared_ptr<User> UserManager::getUserByName(const std::string& name)
{
    // 加锁确保线程安全，防止查找过程中用户数据被修改
    std::lock_guard<std::mutex> lock(users_mutex);

    // 在用户映射表中查找指定用户名
    auto it = registered_users.find(name);

    // 检查用户是否存在
    if (it != registered_users.end())
    {
        // 返回用户对象的共享指针（副本）
        // 注意：返回副本会导致外部修改不影响原始数据
        return std::make_shared<User>(it->second);
    }

    return nullptr; // 未找到用户，返回空指针
}

/*=================================================聊天室类=================================================*/
/**
 * 向聊天室中添加新的客户端连接
 * @param client 客户端连接的智能指针
 */
void ChatRoom::addClient(std::shared_ptr<ClientConnection> client)
{
    // 加锁保护客户端列表操作
    std::lock_guard<std::mutex> lock(clients_mutex);
    // 将客户端添加到在线列表中，以socket文件描述符为键
    online_clients[client->getSocketFd()] = client;
}

/**
 * 从聊天室中移除指定的客户端连接
 * @param fd 要移除的客户端文件描述符
 */
void ChatRoom::removeClient(int fd) {
    // 使用锁确保线程安全地操作在线客户端列表
    std::lock_guard<std::mutex> lock(clients_mutex);
    // 从在线客户端列表中删除指定文件描述符的客户端
    online_clients.erase(fd);
}

/**
 * 向聊天室内的所有在线客户端广播消息，排除指定的文件描述符
 *
 * @param message 要广播的消息内容
 * @param exclude_fd 需要排除的文件描述符，不向该描述符发送消息
 */
void ChatRoom::broadcastMessage(const std::string& message, int exclude_fd)
{
    // 加锁保护在线客户端列表
    std::lock_guard<std::mutex> lock(clients_mutex);

    // 遍历所有在线客户端，向除exclude_fd外的所有客户端发送消息
    for (const auto& pair : online_clients)
    {
        if (pair.first != exclude_fd)
        {
            pair.second->send(message);
        }
    }
}

/**
 * 向指定用户名的在线用户发送私聊消息
 *
 * @param username 目标用户的用户名
 * @param message 要发送的消息内容
 *
 * 该函数会遍历在线客户端列表，找到匹配的在线用户，
 * 并通过其连接对象发送消息。如果找到目标用户并成功发送，
 * 函数会立即返回。
 */
void ChatRoom::sendMessageToUser(const std::string& username, const std::string& message)
{
    std::lock_guard<std::mutex> lock(clients_mutex);
    // 遍历在线客户端列表查找目标用户
    for (const auto& pair : online_clients)
    {
        if (pair.second->isLoggedIn() && pair.second->getLoggedInUser()->getName() == username)
        {
            // 发送消息并立即返回
            pair.second->send(message);
            return;
        }
    }
}

/**
 * 获取当前聊天室中所有在线用户的用户名列表
 * @return 包含所有在线用户名的向量容器
 */
std::vector<std::string> ChatRoom::getOnlineUsernames()
{
    std::lock_guard<std::mutex> lock(clients_mutex); // 自动加锁确保线程安全
    std::vector<std::string> usernames;

    // 遍历在线用户列表，筛选已登录用户
    for (const auto& pair : online_clients)
    {
        if (pair.second->isLoggedIn())
        {
            // 添加已登录用户的名称到结果列表
            usernames.push_back(pair.second->getLoggedInUser()->getName());
        }
    }

    // 返回包含所有在线用户名称的结果
    return usernames;
}

/*=================================================命令处理类=================================================*/
void CommandProcessor::myregister(std::shared_ptr<ClientConnection> sender, std::istringstream& iss, UserManager& user_manager)
{
    std::string name, password, describe;
    if (iss >> name >> password)// 尝试读取用户名和密码
    {
        std::getline(iss, describe); // 读取行的其余部分作为描述，不用iss>>describe是防止描述有空格
        if (!describe.empty() && describe[0] == ' ')
        {
            describe.erase(0, 1); // 移除前导空格
        }
        if (describe.empty())
        {
            describe = "无";
        }
        if (user_manager.registerUser(name, password, describe))
        {
            sender->send("注册成功！\n");
        }
        else
        {
            sender->send("注册失败：用户名已存在。\n");
        }
    }
    else
    {
        sender->send("用法: /register <用户名> <密码> [描述]\n");
    }
}

void CommandProcessor::mylogin(std::shared_ptr<ClientConnection> sender, std::istringstream& iss, UserManager& user_manager, ChatRoom& chat_room)
{
    std::string name, password;
    if (iss >> name >> password)
    {
        std::shared_ptr<User> user = user_manager.loginUser(name, password);
        if (user)
        {
            sender->setLoggedInUser(user); // 设置客户端的登录用户
            sender->send("登录成功！\n");
            chat_room.addClient(sender); // 将登录成功的客户端添加到聊天室在线列表
            chat_room.broadcastMessage(user->getName() + " 已上线。\n", sender->getSocketFd()); // 广播上线消息
        }
        else
        {
            sender->send("登录失败：用户名或密码错误。\n");
        }
    }
    else
    {
        sender->send("用法: /login <用户名> <密码>\n");
    }
}

void CommandProcessor::mymsg(std::shared_ptr<ClientConnection> sender, std::istringstream& iss, ChatRoom& chat_room)
{
    if (!sender->isLoggedIn())
    {
        sender->send("请先登录才能发送私聊消息。\n");
        return;
    }
    std::string target_username;
    std::string message_content;
    if (iss >> target_username)
    {
        std::getline(iss, message_content);
        if (!message_content.empty() && message_content[0] == ' ')
        {
            message_content.erase(0, 1);
        }
        // 格式化私聊消息
        std::string formatted_msg = "[" + sender->getLoggedInUser()->getName() + " 私聊]: " + message_content + "\n";
        chat_room.sendMessageToUser(target_username, formatted_msg);
        sender->send("私聊消息已发送给 " + target_username + "。\n");
    }
    else
    {
        sender->send("用法: /msg <目标用户名> <消息内容>\n");
    }
}
void CommandProcessor::myupload(std::shared_ptr<ClientConnection> sender, std::istringstream& iss) {
    std::string filename_str;
    iss >> filename_str; // 客户端发送的上传文件名

    if (!sender->isLoggedIn())
    {
        sender->send("请先登录才能上传文件。\n");
        return;
    }

    if (filename_str.empty()) {
        sender->send("错误: 请指定要上传的文件名。\n");
        return;
    }

    std::cout << "收到客户端 " << sender->getLoggedInUser()->getName() << " 的文件上传请求: " << filename_str << std::endl;

    FileHeader header;

    ssize_t bytes_read = sender->getclientSocket()->recvAll(&header, sizeof(FileHeader)); // 使用 client_socket 的 recvAll
    if (bytes_read != sizeof(FileHeader))
    {
        sender->send("错误: 接收文件头失败或连接已关闭。\n");
        std::cerr << "接收文件头失败，期望 " << sizeof(FileHeader) << " 字节，实际 " << bytes_read << " 字节。\n";
        return;
    }

    std::string received_filename = header.getFilename();
    long long filesize = header.filesize;

    std::string save_filepath = "uploads/" + received_filename; // 保存到uploads目录

    std::ofstream output_file(save_filepath, std::ios::binary);
    if (!output_file.is_open())
    {
        sender->send("错误: 服务器无法创建文件 " + save_filepath + " 来保存上传。\n");
        std::cerr << "无法打开文件 " << save_filepath << " 进行写入。\n";
        return;
    }

    sender->send("服务器: 准备接收文件 " + received_filename + " (大小: " + std::to_string(filesize) + "字节)\n");
    std::cout << "服务器: 准备接收文件 " << received_filename << " (大小: " << std::to_string(filesize) << "字节)\n";

    char buffer[4096];
    long long received_bytes = 0;
    while (received_bytes < filesize)
    {
        ssize_t bytes_to_recv = std::min((long long)sizeof(buffer), filesize - received_bytes);
        ssize_t bytes_received = sender->getclientSocket()->recvAll(buffer, bytes_to_recv); // 使用 client_socket 的 recvAll
        if (bytes_received > 0)
        {
            output_file.write(buffer, bytes_received);
            received_bytes += bytes_received;
        }
        else if (bytes_received == 0)
        {
            sender->send("错误: 客户端断开连接，文件上传中断。\n");
            std::cerr << "客户端断开连接，文件上传中断。\n";
            output_file.close();
            return;
        }
        else// bytes_received < 0
        {
            perror("接收文件数据失败");
            sender->send("错误: 接收文件数据失败。\n");
            output_file.close();
            return;
        }
    }

    output_file.close();
    sender->send("文件上传完成。\n");
    std::cout << "文件上传完成: " << save_filepath << "\n";
}


void CommandProcessor::mydownload(std::shared_ptr<ClientConnection> sender, std::istringstream& iss)
{
    std::string filename_str;
    iss >> filename_str;

    if (!sender->isLoggedIn())
    {
        sender->send("请先登录才能下载文件。\n");
        return;
    }

    if (filename_str.empty())
    {
        sender->send("错误: 请指定要下载的文件名。\n");
        return;
    }

    std::cout << "收到客户端 " << sender->getLoggedInUser()->getName() << " 的文件下载请求: " << filename_str << std::endl;

    std::string filepath_to_send = "downloads/" + filename_str;
    std::ifstream input_file(filepath_to_send, std::ios::binary | std::ios::ate);
    if (!input_file.is_open())
    {
        sender->send("错误: 服务器上找不到文件 " + filename_str + "。\n");
        std::cerr << "无法打开文件 " << filepath_to_send << " 进行读取。\n";
        return;
    }

    long long filesize = input_file.tellg();
    input_file.seekg(0, std::ios::beg);

    // 构建文件头
    FileHeader header;
    header.setFilename(filename_str);
    header.filesize = filesize;

    // 先发送一个特殊标记，告诉客户端接下来是文件下载
    std::string start_download_cmd = "/download_start " + filename_str + "\n";
    if (sender->getclientSocket()->sendAll(start_download_cmd.c_str(), start_download_cmd.length()) < 0) {
        perror("发送 download_start 命令失败");
        sender->send("错误: 服务器发送下载启动命令失败。\n");
        input_file.close();
        return;
    }
    std::cout << "服务器已发送下载启动命令: " << start_download_cmd;


    // 接着发送文件头
    if (sender->getclientSocket()->sendAll(&header, sizeof(FileHeader)) < 0) {
        perror("发送文件头失败");
        sender->send("错误: 服务器发送文件头失败。\n");
        input_file.close();
        return;
    }
    std::cout << "服务器已发送文件头 (文件名: " << filename_str << ", 大小: " << filesize << "字节)\n";

    // 然后发送文件内容
    char buffer[4096];
    long long sent_bytes = 0;
    while (sent_bytes < filesize)
    {
        input_file.read(buffer, sizeof(buffer));
        ssize_t bytes_read = input_file.gcount();
        if (bytes_read > 0)
        {
            ssize_t bytes_sent = sender->getclientSocket()->sendAll(buffer, bytes_read);
            if (bytes_sent < 0)
            {
                perror("发送文件数据失败");
                sender->send("错误: 服务器发送文件数据失败。\n");
                input_file.close();
                return;
            }
            sent_bytes += bytes_sent;
        }
        else if (bytes_read == 0)
        {
            break; // 文件读取完毕
        }
        else
        {
            perror("读取本地文件失败");
            sender->send("错误: 服务器读取本地文件失败。\n");
            input_file.close();
            return;
        }
    }

    input_file.close();
    sender->send("文件下载完成。\n");
    std::cout << "文件下载完成: " << filepath_to_send << "\n";
}

void CommandProcessor::processCommand(std::shared_ptr<ClientConnection> sender, const std::string& command_message,
    UserManager& user_manager, ChatRoom& chat_room, EpollET& epoll_instance)
{
    std::istringstream iss(command_message); // 将命令消息放入字符串流
    std::string command;
    iss >> command; // 读取命令

    if (command == "/register")
    {
        CommandProcessor::myregister(sender, iss, user_manager);
    }
    else if (command == "/login")
    {
        CommandProcessor::mylogin(sender, iss, user_manager, chat_room);
    }
    else if (command == "/msg")
    {
        CommandProcessor::mymsg(sender, iss, chat_room);
    }
    else if (command == "/upload") // 处理文件上传命令
    {
        CommandProcessor::myupload(sender, iss);
    }
    else if (command == "/download") // 处理文件下载命令
    {
        CommandProcessor::mydownload(sender, iss);
    }
    else if (command == "/logout")
    {
        if (sender->isLoggedIn())
        {
            chat_room.removeClient(sender->getSocketFd());
            std::string user_name = sender->getLoggedInUser()->getName();
            sender->setLoggedInUser(nullptr);
            sender->send("您已退出登录。\n");
            chat_room.broadcastMessage(user_name + " 已退出聊天室。\n");
        }
        else
        {
            sender->send("您尚未登录。\n");
        }
    }
    else if (command == "/list")
    {
        if (!sender->isLoggedIn())
        {
            sender->send("请先登录才能查看在线用户。\n");
            return;
        }
        std::vector<std::string> online_users = chat_room.getOnlineUsernames(); // 获取在线用户列表
        std::string online_list = "当前在线用户:\n";
        for (const std::string& user : online_users) {
            online_list += "- " + user + "\n";
        }
        sender->send(online_list); // 发送在线用户列表给请求者
    }

    else
    {
        sender->send("未知命令。\n");
    }
}


/*=================================================服务器主类=================================================*/
std::string Server::getCurrentTime()
{
    auto now = std::chrono::system_clock::now();
    std::time_t current_time_t = std::chrono::system_clock::to_time_t(now);
    std::stringstream ss;
    // 格式化时间为 YYYY-MM-DD HH:MM:SS
    ss << std::put_time(std::localtime(&current_time_t), "%Y-%m-%d %H:%M:%S");
    return ss.str();
}

std::string Server::formatChatMessage(const std::string& username, const std::string& message)
{
    return "[" + getCurrentTime() + "] " + username + ": " + message + "\n";
}

Server::Server(const char* ip, uint16_t port, const std::string& user_data_file)
{
    server_socket = std::make_shared<Socket>(); // 创建服务器Socket
    std::shared_ptr<InetAddress> serverAddr = std::make_shared<InetAddress>(ip, port);
    server_socket->bind(&(*serverAddr)); // 绑定服务器地址
    server_socket->listen(10); // 开始监听
    server_socket->setnonblocking(); // 设置为非阻塞模式

    epoll_instance = std::make_shared<EpollET>(); // 创建Epoll实例
    epoll_instance->add_fd(server_socket->getFd(), EPOLLIN | EPOLLET); // 将服务器Socket添加到Epoll，监听可读和边缘触发事件

    user_manager = std::make_shared<UserManager>(user_data_file); // 创建用户管理器
    chat_room = std::make_shared<ChatRoom>(); // 创建聊天室
    command_processor = std::make_shared<CommandProcessor>(); // 创建命令处理器

    std::cout << "服务器已启动，监听于 " << ip << ":" << port << std::endl;
}

void Server::start()
{
    while (true)
    {
        std::vector<epoll_event> events = epoll_instance->poll(-1); // 阻塞等待I/O事件发生
        for (const auto& event : events)
        {
            int fd = event.data.fd; // 获取事件对应的文件描述符
            if (fd == server_socket->getFd())
            {
                InetAddress client_addr;
                int client_fd = server_socket->accept(&client_addr); // 接受客户端连接
                if (client_fd >= 0)
                {
                    std::shared_ptr<Socket> client_sock = std::make_shared<Socket>(client_fd);
                    client_sock->setnonblocking(); // 设置客户端Socket为非阻塞模式
                    epoll_instance->add_fd(client_fd, EPOLLIN | EPOLLET); // 将客户端Socket添加到Epoll
                    active_connections[client_fd] = std::make_shared<ClientConnection>(client_sock); // 创建并存储ClientConnection对象
                    active_connections[client_fd]->send("欢迎来到聊天室！请输入 /register 或 /login 进行操作。\n");
                    std::cout << "新客户端连接: 文件描述符 " << client_fd << std::endl;
                }
            }
            else if (event.events & EPOLLIN)// 处理可读事件
            {
                std::shared_ptr<ClientConnection> client = active_connections[fd];
                if (!client)
                {
                    std::cerr << "错误: 在活跃连接中未找到客户端文件描述符 " << fd << "。" << std::endl;
                    close(fd);
                    epoll_ctl(epoll_instance->getEpfd(), EPOLL_CTL_DEL, fd, nullptr); // 从Epoll中删除该fd
                    continue;
                }

                std::string received_data;
                char buffer[BUFSIZ];
                while (true)
                {
                    bzero(buffer, sizeof(buffer));
                    ssize_t bytes_recv = recv(fd, buffer, BUFSIZ - 1, 0); // 接收数据
                    if (bytes_recv > 0)
                    {
                        buffer[bytes_recv] = '\0'; // 确保数据以null结尾
                        received_data += buffer;
                    }
                    else if (bytes_recv == -1 && errno == EINTR)
                    {
                        std::cout << "系统中断，继续读取数据" << std::endl;
                        continue;
                    }
                    else if (bytes_recv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
                    {
                        break;
                    }
                    else if (bytes_recv == 0)// 客户端断开连接
                    {
                        std::cout << "客户端 " << fd << " 已断开连接" << std::endl;
                        if (client->isLoggedIn())
                        {
                            chat_room->broadcastMessage(client->getLoggedInUser()->getName() + " 已下线。\n"); // 广播下线消息
                        }
                        chat_room->removeClient(fd); // 从聊天室移除客户端
                        active_connections.erase(fd); // 从活跃连接中移除
                        close(fd); // 关闭文件描述符
                        epoll_ctl(epoll_instance->getEpfd(), EPOLL_CTL_DEL, fd, nullptr); // 从Epoll中删除该fd
                        break;
                    }
                    else {
                        // 其他错误
                        perror("接收数据错误");
                        if (client->isLoggedIn()) {
                            chat_room->broadcastMessage(client->getLoggedInUser()->getName() + " 异常下线。\n");
                        }
                        chat_room->removeClient(fd);
                        active_connections.erase(fd);
                        close(fd);
                        epoll_ctl(epoll_instance->getEpfd(), EPOLL_CTL_DEL, fd, nullptr);
                        break;
                    }
                }

                if (!received_data.empty())
                {
                    if (!received_data.empty() && received_data[0] == '/')// 命令消息
                    {
                        std::string command_to_process = received_data;
                        if (!command_to_process.empty() && command_to_process.back() == '\n') command_to_process.pop_back();
                        if (!command_to_process.empty() && command_to_process.back() == '\r') command_to_process.pop_back();
                        command_processor->processCommand(client, command_to_process, *user_manager, *chat_room, *epoll_instance);
                    }
                    else// 普通消息
                    {
                        std::string message_to_process = received_data;
                        if (!message_to_process.empty() && message_to_process.back() == '\n') message_to_process.pop_back();
                        if (!message_to_process.empty() && message_to_process.back() == '\r') message_to_process.pop_back();

                        if (client->isLoggedIn())
                        {
                            std::string formatted_msg = formatChatMessage(client->getLoggedInUser()->getName(), message_to_process);
                            chat_room->broadcastMessage(formatted_msg); // 广播格式化后的消息
                        }
                        else
                        {
                            client->send("请先登录才能发送消息。\n");
                        }
                    }
                }
            }
        }
    }
}

// 主函数入口
int main()
{
    Server chat_server("0.0.0.0", 8080, "user.txt"); // 创建服务器实例
    chat_server.start(); // 启动服务器
    return 0;
}